package com.ark.cloud.java.persistence.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

/**
 * @author by lk
 * @date 2021-08-07 13:33
 */
public class ByteUtils {

    private static final Logger logger = LoggerFactory.getLogger(ByteUtils.class);

    /**
     * 数组转字符串
     * @param src
     * @return
     */
    public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     *byte 转 无符号int
     * @param a
     * @return
     */
    public static int byteToUnsignedInt (byte a){
        int i = a & 0xff;
        return i;
    }

    /**
     * byte数组转double
     * @param b
     * @return
     */
    public static double getDouble(byte[] b) {
        long m;
        m = b[0];
        m &= 0xff;
        m |= ((long) b[1] << 8);
        m &= 0xffff;
        m |= ((long) b[2] << 16);
        m &= 0xffffff;
        m |= ((long) b[3] << 24);
        m &= 0xffffffffL;
        m |= ((long) b[4] << 32);
        m &= 0xffffffffffL;
        m |= ((long) b[5] << 40);
        m &= 0xffffffffffffL;
        m |= ((long) b[6] << 48);
        m &= 0xffffffffffffffL;
        m |= ((long) b[7] << 56);
        return Double.longBitsToDouble(m);
    }

    /**
     * 字节数组到long的转换.
     */
    public static long byteToLong(byte[] b) {
        long s = 0;
        long s0 = b[0] & 0xff;// 最低位
        long s1 = b[1] & 0xff;
        long s2 = b[2] & 0xff;
        long s3 = b[3] & 0xff;
        long s4 = b[4] & 0xff;// 最低位
        long s5 = b[5] & 0xff;
        long s6 = b[6] & 0xff;
        long s7 = b[7] & 0xff;

        // s0不变
        s1 <<= 8;
        s2 <<= 16;
        s3 <<= 24;
        s4 <<= 8 * 4;
        s5 <<= 8 * 5;
        s6 <<= 8 * 6;
        s7 <<= 8 * 7;
        s = s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7;
        return s;
    }

    public static long byteToLong2(byte [] b){
        long l = 0;
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        DataInputStream dis = new DataInputStream(bais);
        try {
            l = dis.readLong();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return l;
    }


    /**
     * 字节数组到int的转换.
     */
    public static int byteToInt(byte[] b) {
        int s = 0;
        int s0 = b[0] & 0xff;// 最低位
        int s1 = b[1] & 0xff;
        int s2 = b[2] & 0xff;
        int s3 = b[3] & 0xff;
        s3 <<= 24;
        s2 <<= 16;
        s1 <<= 8;
        s = s0 | s1 | s2 | s3;
        return s;
    }
    /**
     * int到字节数组的转换.
     */
    public static byte[] intToByte(int number) {
        int temp = number;
        byte[] b = new byte[4];
        for (int i = 0; i < b.length; i++) {
            b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最低位
            temp = temp >> 8;// 向右移8位
        }
        return b;
    }

    /**
     * unsigned int
     * byte 数组转无符号int
     * @param
     * @return
     */
    public static int bytesArrayToIntLittle(byte[] src, int offset) {
        int value;
        value = (int) ((src[offset] & 0xFF)
                | ((src[offset + 1] & 0xFF) << 8)
                | ((src[offset + 2] & 0xFF) << 16)
                | ((src[offset + 3] & 0xFF) << 24));
        return value;
    }

    /**
     * 字节数组到short的转换.
     */
    public static short byteToShort(byte[] b) {
        short s = 0;
        short s0 = (short) (b[0] & 0xff);// 最低位
        short s1 = (short) (b[1] & 0xff);
        s1 <<= 8;
        s = (short) (s0 | s1);
        return s;
    }

    /**
     * char到字节数组的转换.
     */
    public static byte[] charToByte(char c){
        byte[] b = new byte[2];
        b[0] = (byte) ((c & 0xFF00) >> 8);
        b[1] = (byte) (c & 0xFF);
        return b;
    }

    // float转换为byte[4]数组
    public static byte[] floatToByte(float f) {
        // 把float转换为byte[]
        int fbit = Float.floatToIntBits(f);

        byte[] b = new byte[4];
        for (int i = 0; i < 4; i++) {
            b[i] = (byte) (fbit >> (24 - i * 8));
        }

        // 翻转数组
        int len = b.length;
        // 建立一个与源数组元素类型相同的数组
        byte[] dest = new byte[len];
        // 为了防止修改源数组，将源数组拷贝一份副本
        System.arraycopy(b, 0, dest, 0, len);
        byte temp;
        // 将顺位第i个与倒数第i个交换
        for (int i = 0; i < len / 2; ++i) {
            temp = dest[i];
            dest[i] = dest[len - i - 1];
            dest[len - i - 1] = temp;
        }

        return dest;

    }


    /**
     * double到字节数组的转换.
     */
    public static byte[] doubleToByte(double num) {
        byte[] b = new byte[8];
        long l = Double.doubleToLongBits(num);
        for (int i = 0; i < 8; i++) {
            b[i] = new Long(l).byteValue();
            l = l >> 8;
        }
        return b;
    }

    /**
     * long类型转成byte数组
     */
    public static byte[] longToByte(long number) {
        long temp = number;
        byte[] b = new byte[8];
        for (int i = 0; i < b.length; i++) {
            b[i] = new Long(temp & 0xff).byteValue();// 将最低位保存在最低位 temp = temp
            // >> 8;// 向右移8位
        }
        return b;
    }

    public static byte [] longToByte2(long number){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            dos.writeLong(number);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return baos.toByteArray();
    }


    /**
     * 字节数组到float的转换.
     */
    public static float byteToFloat(byte[] b) {
        // 4 bytes
        int accum = 0;
        for (int shiftBy = 0; shiftBy < 4; shiftBy++ ) {
            accum |= (b[shiftBy] & 0xff) << shiftBy * 8;
        }
        return Float.intBitsToFloat(accum);
    }


    /**
     * 分割byte[]
     * @param bytes         被分割的数组
     * @param startIndex    分割的数组下标起点
     * @param length        被分割的数组长度
     * @return  被分割好的byte []
     */
    public static byte[] splitArray(byte [] bytes,int startIndex,int length){
        if (startIndex + length >  bytes.length){
            throw new RuntimeException("分割的起点和长度之和必须小于数组的长度！");
        }

        byte [] splitArray = new byte[length];
        int j = 0;
        for (int i = startIndex; i < startIndex + length; i ++){
            splitArray[j] = bytes[i];
            j ++;
        }
        return splitArray;
    }

    /**
     * 将小数组的值赋值到大数组中
     * @param big
     * @param small
     * @param startIndex 赋值的大数组的起点坐标
     * @param endIndex   赋值的大数组的终点坐标
     * @return
     */
    public static byte[] putArray(byte [] big,byte[] small,int startIndex,int endIndex){
        if (big.length < startIndex && big.length < endIndex){
            throw new RuntimeException("起点和结束点必须小于大数组的长度！");
        }
        if (small.length > big.length && small.length+(endIndex - startIndex) > big.length){
            throw new RuntimeException("小数组的长度及总长度不能大于大数组的长度！");
        }

        for (int i = 0; i < small.length; i ++){
            big [startIndex] = small[i];
            startIndex++;
        }
        return big;
    }

    public static double[] byteArray2DoubleArray(byte[] input) {
        int length = input.length / 8;
        double[] doubles = new double[length];
        for (int i = 0; i < length; i++) {
            double aDouble = getDouble(splitArray(input, i * 8, 8));
            doubles[i] = aDouble;
        }
        return doubles;
    }




    /**
     * byte数组转二进制字符串
     * @param b
     * @return
     */
    public static String toBitString(final byte[] b) {
        final char[] bits = new char[8 * b.length];
        for (int i = 0; i < b.length; i++) {
            final byte byteval = b[i];
            int bytei = i << 3;
            int mask = 0x1;
            for (int j = 7; j >= 0; j--) {
                final int bitval = byteval & mask;
                if (bitval == 0) {
                    bits[bytei + j] = '0';
                } else {
                    bits[bytei + j] = '1';
                }
                mask <<= 1;
            }
        }
        return String.valueOf(bits);
    }
    /**
     * 转换String为byte[]
     * @param str
     * @return
     */
    public static byte[] StrToBytesArr(String str) {
        if(str == null){
            return null;
        }
        char[] chars = str.toCharArray();

        byte[] rst = charArrayToBytes(chars);

        return rst;
    }

    /**
     * 转换字符数组为定长byte[]
     * @param chars              字符数组
     * @return 若指定的定长不足返回null, 否则返回byte数组
     */
    public static byte[] charArrayToBytes(char[] chars){
        if(chars == null) {
            return null;
        }

        int iCharCount = chars.length;
        byte[] rst = new byte[iCharCount*2];
        int i = 0;
        for( i = 0; i < iCharCount; i++){
            rst[i*2] = (byte)(chars[i] & 0xFF);
            rst[i*2 + 1] = (byte)(( chars[i] & 0xFF00 ) >> 8);
        }
        return rst;
    }

    public static byte[] charArrayToBytes2(char[] chars) {
        Charset cs = Charset.forName("UTF-8");
        CharBuffer cb = CharBuffer.allocate(chars.length);
        cb.put(chars);
        cb.flip();
        ByteBuffer bb = cs.encode(cb);
        return bb.array();
    }


    /**
     * char [] 转 byte[]
     * @param chars
     * @return
     */
    public static byte[] charArrToBytesArr(char[] chars) {
        if (chars == null) {
            return null;
        }
        byte[] bs2 = new byte[chars.length];
        for (int i = 0; i < chars.length; i++) {
            byte b = (byte) chars[i];
            bs2[i] = b;
        }
        return bs2;
    }

    /**
     *将char转为单字节，只能解析单字符
     * @param c
     * @return
     */
    public static byte[] charToOneByte(char c){
        String s = String.valueOf(c);
        byte[] bytes = new byte[1];
        try {
            bytes = s.getBytes("iso8859-1");
        } catch (UnsupportedEncodingException e) {
            logger.error(s+":"+"不能解析，只能解析单字符！");
            throw new RuntimeException("只能解析单字符！");
        }
        return bytes;
    }

    /**
     * byte数组转char数组
     * @param bytes
     * @return
     */
    public static char[] byteArrayToCharArray (byte[] bytes) {
        Charset cs = Charset.forName ("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate (bytes.length);
        bb.put (bytes);
        bb.flip ();
        CharBuffer cb = cs.decode (bb);
        return cb.array();
    }




    /**
     * short数组转byte数组
     * @param src
     * @return
     */
    public static byte[] shortArrayToByteArray(short[] src) {
        int count = src.length;
        byte[] dest = new byte[count << 1];
        for (int i = 0; i < count; i++) {
            dest[i * 2] = (byte) (src[i] >> 8);
            dest[i * 2 + 1] = (byte) (src[i] >> 0);
        }
        return dest;
    }
    public static byte[] shortArrayToByteArray2(short[] src){
        byte [] bytes = new byte[2];
        byte [] bytes1 = new byte[src.length << 1];
        for (int i =0;i < src.length;i++){
            bytes = shortToByte(src[i]);
            ByteUtils.putArray(bytes1,bytes,i*2,i*2+1);
        }
        return bytes1;
    }

    /**
     * short到字节数组的转换.
     */
    public static byte[] shortToByte(short number) {
        int temp = number;
        byte[] b = new byte[2];
        for (int i = 0; i < b.length; i++) {
            b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最低位
            temp = temp >> 8;// 向右移8位
        }
        return b;
    }

    /**
     * byte数组转为short数组,byte数组长度必须是2的倍数
     * @param src
     * @return
     */
    public static short[] byteArrayToShortArray(byte[] src) {

        int count = src.length >> 1;
        short[] dest = new short[count];
        for (int i = 0; i < count; i++) {
            dest[i] = (short) (src[i * 2] << 8 | src[2 * i + 1] & 0xff);
        }
        return dest;
    }


    public static byte[] doubleArrayToByteArray2(double[] input){
        byte [] bytes = new byte[input.length * 8];
        byte [] elementArray = new byte[8];
        for (int i = 0; i < input.length; i++){
            elementArray = doubleToByte(input[i]);
            ByteUtils.putArray(bytes,elementArray,i * 8,i * 8 + 7);
        }
        return bytes;
    }

    public static byte[] double2ArrayToByteArray2(double[][] input){
        int lenY = input.length;
        int lenX = input[0].length;
        byte [] bytes = new byte[lenY * lenX * 8];
        byte [] elementArray = new byte[8];
        for (int i = 0; i < lenY; i++){
            byte [] temp = new byte[lenX * 8];
            for (int j = 0; j < lenX; j++) {
                elementArray = doubleToByte(input[i][j]);
                ByteUtils.putArray(temp, elementArray, j * 8, j * 8 + 7);
            }
            ByteUtils.putArray(bytes, temp, i * lenX * 8, i * lenX * 8 + lenX - 1);
        }
        return bytes;
    }

    public static double[][] byteArray2DoubleArray2(byte[] input,int lenX,int lenY) {
        double[][] doubles = new double[lenX][lenY];
        int num = 0;
        for (int i = 0; i < lenX; i++) {
            for (int j = 0; j < lenY; j++) {
                double aDouble = getDouble(splitArray(input, num * 8, 8));
                doubles[i][j] = aDouble;
                num++;
            }
        }
        return doubles;
    }

    public static String byte2String(byte[] data) {
       char[] hexArray = "0123456789ABCDEF".toCharArray();
       char[] hexChars = new char[data.length * 2];

       for (int j = 0; j < data.length; j++) {
               int v = data[j] & 0xFF;
               hexChars[j * 2] = hexArray[v >>> 4];
               hexChars[j * 2 + 1] = hexArray[v & 0x0F];
           }

        String result = new String(hexChars);
        result = result.replace(" ", "");
        return result;
    }

    public static String char2String(char[] data) {

        if(data == null) {
            return null;
        }

        int iCharCount = data.length;
        byte[] rst = new byte[iCharCount*2];
        int i = 0;
        for( i = 0; i < iCharCount; i++){
            rst[i*2] = (byte)(data[i] & 0xFF);
            rst[i*2 + 1] = (byte)(( data[i] & 0xFF00 ) >> 8);
        }

        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[rst.length * 2];

        for (int j = 0; j < rst.length; j++) {
            int v = rst[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }

        String result = new String(hexChars);
        result = result.replace(" ", "");
        return result;
    }


    public static byte[] hexTobytes(String hex) {
        if (hex.length() < 1) {
            return null;
        } else {
            byte[] result = new byte[hex.length() / 2];
            int j = 0;
            for(int i = 0; i < hex.length(); i+=2) {
                result[j++] = (byte)Integer.parseInt(hex.substring(i,i+2), 16);
            }
            return result;
        }
    }

    public static String bytes2HexString(byte[] b) {
        String r = "";

        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase();
        }

        return r;
    }


    public static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if(hex.length() < 2){
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }


    public static String byteArrayToCharArrayToString (byte[] bytes) {
        Charset cs = Charset.forName ("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate (bytes.length);
        bb.put (bytes);
        bb.flip ();
        CharBuffer cb = cs.decode (bb);
        return String.valueOf(cb.array());
    }

    public static String byteArrayToCharArrayToStringGBK (byte[] bytes) {
        Charset cs = Charset.forName ("GBK");
        ByteBuffer bb = ByteBuffer.allocate (bytes.length);
        bb.put (bytes);
        bb.flip ();
        CharBuffer cb = cs.decode (bb);
        return String.valueOf(cb.array());
    }

}
